#include "socketHandling.h"

int getNextConnectionId(int * connectionState, int size)
{
  int i = 0;
  for (i=0;i<size;i++)
    {
      if (connectionState[i] == 0)
	return i;
    }
  WG("%s","Connection table is full ... no more connection allowed");
  return -1;
}

int waitForConnection(int socketFd, int* connectionState, int connectionId, pthread_t* threads)
{
  int clientSocketFd;
  struct sockaddr_in* clientSin = malloc(sizeof(struct sockaddr_in));
  int clientLen = sizeof(struct sockaddr_in); 
  clientSocketFd = accept(socketFd, (struct sockaddr*) clientSin, &clientLen);
  DBG("accepting new client, socketFd = %d\n", clientSocketFd);
  if (clientSocketFd == -1)
    {
      perror("accept()");
      return 0;
    }
  peerConnectionData* connectionData = malloc(sizeof(peerConnectionData));
  connectionData->connectionInfo = clientSin;
  connectionData->connectionNumber = connectionId;
  connectionData->socketFd = clientSocketFd;
  connectionData->connectionState = connectionState;
  pthread_create(&threads[connectionId], NULL, (void*) peerDialog, (void*) connectionData);
  return 1;
}

/* Reception du message du Peer, calcule de la réponse */
void peerDialog(void* cd)
{
  peerConnectionData* connectionData = (peerConnectionData*) cd;
  LG("new connection ( %d )\n", connectionData->connectionNumber);
  message* m;
  m = waitForCommand(connectionData);
  if (m!=NULL)
    {
      LG("%s","Received message\n");
      LG("type : %d\n", m->type);
      LG("data = %s\n", m->data);
      /* Calcul de la réponse */
      char* ip = strdup(inet_ntoa(connectionData->connectionInfo->sin_addr));
      char* response = computeMessage(m,ip);
      write(connectionData->socketFd, response, strlen(response));
      
      /* Libération mémoire, cloture de la connexion */
      free(m);
      free(response);
      // fermeture du socket
      shutdown(connectionData->socketFd,SHUT_RDWR);
      // on rend le numéro de connection disponible
      connectionData->connectionState[connectionData->connectionNumber] = 0; 
    }
  else
    {
      char* response = strdup("Error - Timeout or incorrect message");
      write(connectionData->socketFd, response, strlen(response));
      free(response);
      shutdown(connectionData->socketFd,SHUT_RDWR);
      connectionData->connectionState[connectionData->connectionNumber] = 0; 
    }
}

message* waitForCommand(peerConnectionData* connectionData)
{
  assert(connectionData != NULL);
  message* m = createMessage(BUFFER_SIZE);
  char buffer[BUFFER_SIZE];
  char tempBuffer[BUFFER_SIZE];
  int sizeRead = 0;
  int (*checkFunction) (message*, char*); 
  int readTimeout = 5000;
  time_t timeout = time(NULL);
  DBG("Wait for command possible values are : %d %d %d %d %d\n", ANNOUNCE, LOOK, GETFILE, UPDATE, UNKNOWN);
  do 
    {
      sizeRead = read(connectionData->socketFd, tempBuffer, BUFFER_SIZE);
      // Aucun message ne peut avoir une taille de 0
      if (sizeRead == 0)
	{
	  free(m);
	  WG("Message read had size 0, dropping connection %d\n", connectionData->connectionNumber);
	  return NULL;
	}
      // Aucun message ne peut avoir un taille supérieur à BUFFER_SIZE
      else if(sizeRead + strlen(buffer) > BUFFER_SIZE)
	{
	  free(m);
	  WG("Message too long, dropping connection %d\n", connectionData->connectionNumber);
	  return NULL;
	}
      strncat(buffer,tempBuffer,sizeRead);
      DBG("Current buffer is : %s \n", buffer);
      // Après timeout read, on considère que le temps d'attente est trop long
      readTimeout--;
      if (readTimeout == 0)
	{
	  free(m);
	  WG("Timeout for connection %d, too much read\n", connectionData->connectionNumber);
	  return NULL;
	}
      if (difftime(time(NULL), timeout) > 30)
	{
	  free(m);
	  WG("Timeout for connection %d, message took more than 30 sec to be complete\n",
	     connectionData->connectionNumber);
	  return NULL;
	}
    }
  while(!getCommand(m,buffer));
  DBG("%s","Switch\n");
  switch(m->type)
    {
    case ANNOUNCE:
      {
	checkFunction = check_announce;
      } break;
    case LOOK:
      {
	checkFunction = check_look;
      } break;
    case GETFILE:
      {
	checkFunction = check_getfile;
      } break;
    case UPDATE:
      {
	checkFunction = check_update;
      } break;
    case UNKNOWN:
      {
	free(m);
	return NULL;
      } break;

    }
  //cleanBuffer(buffer, BUFFER_SIZE);
  //cleanBuffer(tempBuffer, BUFFER_SIZE);
  DBG("%s","WHILE\n");
  int dataComplete = 0;
  dataComplete = checkFunction(m,buffer);
  while(!dataComplete)
    {
      sizeRead = read(connectionData->socketFd, tempBuffer, BUFFER_SIZE);
      strncat(buffer,tempBuffer,sizeRead);
      DBG("buffer = %s \n", buffer);
      DBG("foo = %p \n", checkFunction);
      dataComplete = checkFunction(m,buffer);
      DBG("dataComplete = %d \n", dataComplete);
    }
  if (dataComplete == 2)
    {
      WG("%s","Malformed message\n");
      free(m);
      return NULL;
    }
  else
    {
      DBG("%s","Message correctly formed, exiting waitForCommand\n");
      return m;
    }
}
